﻿using Leafing.Data.SqlEntry;
using System.Text;

namespace DBEntryHelper
{
    public class ModelsGenerator
    {
        private static string? env;
        private static bool bADDT;
        public static void SetEnv(string senv,bool AddT)
        {
            env = senv;
            bADDT = AddT;
        }

        public class ModelBuilder
        {
            private readonly Dictionary<Type, string> _types;
            protected string TableName;
            protected List<DbColumnInfo> InfoList;
            protected StringBuilder Result;

            public ModelBuilder(string tableName, List<DbColumnInfo> list)
            {
                TableName = tableName;
                InfoList = list;

                Result = new StringBuilder();

                _types = new Dictionary<Type, string>
                {
                    {typeof (string), "string?"},
                    {typeof (int), "int"},
                    {typeof (short), "short"},
                    {typeof (long), "long"},
                    {typeof (float), "float"},
                    {typeof (double), "double"},
                    {typeof (DateTime), "DateTime"},
                    {typeof (bool), "bool"},
                    {typeof (TimeSpan), "Time"},
                    {typeof (byte[]), "byte[]"},
                };
            }

            protected string GetTypeName(Type t)
            {
                if (_types.ContainsKey(t))
                {
                    return _types[t];
                }
                return t.ToString();
            }

            protected string GetNullableTypeName(DbColumnInfo info)
            {
                string s = GetTypeName(info.DataType);
                if (info.AllowDBNull && info.DataType.IsValueType)
                {
                    s += "?";
                }
                return s;
            }

            public virtual string Build()
            {
                  Result.Append("[Serializable]\r\n ") ; 
                  Result.Append(" [DbTable(\"" + TableName + "\")]\r\n");
                  if (bADDT)
                  {
                      Result.Append("public class ").Append("T_" +TableName);
                  }
                  else
                  {
                      Result.Append("public class ").Append(TableName);
                  }
                AppendBaseType(TableName);
                foreach (var info in InfoList)
                {
                    if (info.IsKey)
                    {
                        BuildKeyColomn(info);
                        ProcessKeyColumn(info);
                    }
                    else
                    {
                        BuildColumn(info);
                        ProcessColumn(info);
                    }
                }
                Result.Append("}\r\n");
                return Result.ToString();
            }

            protected virtual void AppendInitMethodBody()
            {
                Result.Append(";\r\n");
            }

            protected virtual void AppendBaseType(string tableName)
            {
                Result.Append(" : DbObjectModel<").Append(tableName).Append(">\r\n{\r\n");
            }

            protected virtual void BuildKeyColomn(DbColumnInfo info)
            {
            }

            protected virtual void BuildColumn(DbColumnInfo info)
            {
                Result.Append('\t');
                if (info.AllowDBNull && !info.DataType.IsValueType)
                {
                    Result.Append("[AllowNull] ");
                }
                if (info.DataType == typeof(string) || info.DataType == typeof(byte[]))
                {
                    if (info.ColumnSize < 32768)
                    {
                        Result.Append("[Length(").Append(info.ColumnSize /4).Append(")] ");
                    }
                }
                if (info.IsUnique)
                {
                    Result.Append("[Index(UNIQUE = true)] ");
                }
                Result.Append("public ");
                Result.Append(" ");
                Result.Append(GetNullableTypeName(info));
                Result.Append(" ");
                Result.Append(RenameColumn(info.ColumnName));
                Result.Append(GetColumnBody());
                Result.Append("\r\n");
            }

            private string RenameColumn(string data)
            {
                string newval = "";
                if (data.IndexOf("_") > 0)
                {
                    string[] datas = data.Split('_');
                    foreach (var item in datas)
                    {
                        newval += string.Concat(item[..1].ToUpper(), item.AsSpan(1, item.Length - 1), "_");
                    }
                    newval = newval[..^1];
                }
                else
                {
                    newval = string.Concat(data[..1].ToUpper(), data.AsSpan(1, data.Length - 1));
                }
                return newval;
            }

            protected virtual void ProcessKeyColumn(DbColumnInfo info)
            {
            }

            protected virtual void ProcessColumn(DbColumnInfo info)
            {
            }

            protected virtual string GetColumnBody()
            {
                return " { get; set; }";
            }
        }

        public class ObjectModelBuilder : ModelBuilder
        {
            protected StringBuilder InitMethodBody = new();

            public ObjectModelBuilder(string tableName, List<DbColumnInfo> list)
                : base(tableName, list)
            {
            }

            protected override void AppendInitMethodBody()
            {
                Result.Append("{\r\n");
                if (InitMethodBody.Length > 3)
                {
                    Result.Append(InitMethodBody);
                    Result.Append("\t\treturn this;\r\n");
                }
                Result.Append("\t}\r\n");
            }

            protected override void AppendBaseType(string tableName)
            {
                Result.Append(": IDbObject \r\n{\r\n");
            }

            protected override void BuildKeyColomn(DbColumnInfo info)
            {
                Result.Append(info.IsAutoIncrement ? "\t[DbKey]\r\n" : "\t[DbKey(IsDbGenerate = false)]\r\n");
                BuildColumn(info);
            }

            protected override void ProcessKeyColumn(DbColumnInfo info)
            {
                if (!info.IsAutoIncrement)
                {
                    ProcessColumn(info);
                }
            }

            protected override void ProcessColumn(DbColumnInfo info)
            {
                base.ProcessColumn(info);
                string col = info.ColumnName;
                InitMethodBody.Append("\t\tthis.");
                InitMethodBody.Append(col);
                InitMethodBody.Append(" = ");
                InitMethodBody.Append(col);
                InitMethodBody.Append(";\r\n");
            }

            
             
        }

        public static List<string> GetTableList()
        {
            DataProvider dv = new(env);
            return dv.GetTableNames();
        }

        public static string GenerateModelFromDatabase(string tableName)
        {
            if (tableName.ToLower() == "*")
            {
                var sb = new StringBuilder();
                foreach (var table in GetTableList())
                {
                    string s = GetModel(table);
                    sb.Append(s);
                    sb.Append("\r\n");
                }
                return sb.ToString();
            }
            return GetModel(tableName);
        }

        private static string GetModel(string tableName)
        {  
            DataProvider dv = new( env);
            var list = dv.GetDbColumnInfoList(tableName);
            foreach (var info in list)
            {
                if (info.IsKey && info.IsAutoIncrement && info.ColumnName.ToLower() == "id")
                {
                    return new ModelBuilder(tableName, list).Build();
                }
            }
            return new ObjectModelBuilder(tableName, list).Build();
        }
    }
}
